package com.zhkc.iess.online.test01.server;

import com.zhkc.iess.online.test01.config.NettyServerInitializer2;
import com.zhkc.iess.online.test01.config.PortDefinition;
import com.zhkc.iess.online.test01.config.SubscribeFactoryConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.BindException;
import java.net.InetSocketAddress;
import java.util.Map;

@Component
@Slf4j
public class NettyServer {
    /**
     * boss 线程组用于处理连接工作
     */
    private EventLoopGroup bossGroup;
    /**
     * work 线程组用于数据处理
     */
    private EventLoopGroup ioWorkerGroup;

    /**
     * 端口参数，配置与配置文件中具体参数类型如：
     * netty:
     * port: {6001: A, 6002: B, 6003: C}
     * Netty服务启动后获取到配置参数，监听配置相对应的端口
     */
    @Autowired
    private PortDefinition portDefinition;

    /**
     * 启动Netty Server
     *
     * @throws InterruptedException
     */
    @PostConstruct
    public void start() throws InterruptedException {

        this.bossGroup = new NioEventLoopGroup(2);
        this.ioWorkerGroup = new NioEventLoopGroup();


        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, ioWorkerGroup)
                // 指定Channel
                .channel(NioServerSocketChannel.class)
                //使用指定的端口设置套接字地址
//                .localAddress(new InetSocketAddress(port))
                //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数,如果并发多需要调整大小。参考：https://www.cnblogs.com/kelelipeng/p/17662089.html
                .option(ChannelOption.SO_BACKLOG, 4096)
                //设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, false)
                //设置ByteBuf的分配器。可以配置为PooledByteBufAllocator.DEFAULT以启用内存池。
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                //将小的数据包包装成更大的帧进行传送，提高网络的负载
                .childOption(ChannelOption.TCP_NODELAY, true)
//                .childHandler(new NettyServerInitializer2());
                .childHandler(new NettyServerInitializer2(portDefinition));

        ChannelFuture[] channelFutures;
        Map<Integer, String> ports = portDefinition.getPort();
        channelFutures = new ChannelFuture[ports.size()];
        //绑定多个端口核心代码
        int i = 0;
        try {
            for (Map.Entry<Integer, String> p : ports.entrySet()) {
                final int port = p.getKey();
                // 绑定端口
                ChannelFuture future = bootstrap.bind(new InetSocketAddress(port)).sync();
                channelFutures[i++] = future;
                future.addListener(f -> {
                    if (f.isSuccess()) {
                        log.info("netty 启动成功，端口：{}", port);
                    } else {
                        log.error("netty 启动失败，端口：{}", port);
                    }
                });
            }
        } catch (InterruptedException e) {
            log.error("netty 启动时发生异常-------", e);
        }

        for (ChannelFuture future : channelFutures) {
            if (!future.isSuccess()) {
                Throwable cause = future.cause();
                if (!(cause instanceof BindException)) {
                    throw new IllegalStateException("----------------netty future-启动时发生异常 ", cause);
                }
                log.warn("------------------netty future-启动时发生异常", cause);
            }
        }

        /**
         * 单个端口的逻辑，解析一种协议
         */
//       ChannelFuture future = bootstrap.bind().sync();
//        if (future.isSuccess()) {
//            log.info("---------------------启动 Netty Server------------------");
//
//            //定时打印直接内存（ByteBuf）大小
//            this.ioWorkerGroup.next().scheduleWithFixedDelay(new Runnable() {
//                public void run() {
//                    log.info("Netty occupied direct memory {}KB", PooledByteBufAllocator.DEFAULT.metric().usedDirectMemory() / 1024L);
//                }
//            }, 10L, 10L, TimeUnit.SECONDS);
//            return;
//        }

//        Throwable cause = future.cause();
//        if (!(cause instanceof BindException)) {
//            throw new IllegalStateException("----------------Failed to start netty server at " + this.port, cause);
//        }
//        log.warn("------------------Failed to start netty server at {},will try other port.", this.port, cause);

    }

    @PreDestroy
    public void stopServer() throws InterruptedException {
        this.bossGroup.shutdownGracefully().sync();
        this.ioWorkerGroup.shutdownGracefully().sync();
        log.info("----------------关闭Netty------------------");
    }
}